/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021.
 * Description: support read to acpi data after acpi_subsystem_init()
 * Author: yangzhuohao <yangzhuohao@huawei.com>
 * Create: 2021-01-30
 */

#include <linux/hal/acpi_op.h>
#include "../acpi/internal.h"

#define PROP_NAME_SIZE 128

void acpi_get_acpi_device(acpi_handle handle, struct acpi_device *device, const char *hid)
{
	struct acpi_hardware_id *id;

	INIT_LIST_HEAD(&device->pnp.ids);
	id = kmalloc(sizeof(*id), GFP_KERNEL);
	if (!id)
		return;

	id->id = kstrdup_const(hid, GFP_KERNEL);
	if (!id->id) {
		kfree(id);
		return;
	}
	list_add_tail(&id->list, &device->pnp.ids);

	device->pnp.type.hardware_id = 1;
	device->handle = handle;
	acpi_init_properties(device);

	kfree_const(id->id);
	kfree(id);
}

int acpi_getprop_u32(unsigned int *prop, struct acpi_device *device, const char *name)
{
	int ret;
	const union acpi_object *obj = NULL;

	*prop = 0;
	ret = acpi_dev_get_property(device, name, ACPI_TYPE_INTEGER, &obj);
	if (ret)
		return -ENODATA;
	if (obj->integer.value > U32_MAX)
		return -EOVERFLOW;

	*prop = obj->integer.value;
	return 0;
}

int acpi_getprop_u64(unsigned long long *prop, struct acpi_device *device, const char *name)
{
	int ret;
	const union acpi_object *obj = NULL;

	*prop = 0;
	ret = acpi_dev_get_property(device, name, ACPI_TYPE_INTEGER, &obj);
	if (ret)
		return -ENODATA;

	*prop = obj->integer.value;
	return 0;
}

int acpi_reg_match_string(struct acpi_device *device, const char *name, const char *string)
{
	int ret;
	const union acpi_object *obj = NULL;

	ret = acpi_dev_get_property(device, name, ACPI_TYPE_STRING, &obj);
	if (ret)
		return -ENODATA;

	if (strcmp(string, obj->string.pointer) == 0)
		return 0;

	return -ENODATA;
}

#ifdef CONFIG_RTOS_DROL

static void acpi_getprop_op(unsigned int *op, struct acpi_device *dev, const char *name)
{
	*op = DROL_OP_WRITE | DROL_OP_READ;

	if (acpi_reg_match_string(dev, name, "readonly") >= 0)
		*op = DROL_OP_READ;
	else if (acpi_reg_match_string(dev, name, "writeonly") >= 0)
		*op = DROL_OP_WRITE;
}

static int acpi_getprop_reg(int addr_cells, phys_addr_t *reg, struct acpi_device *dev, const char *name)
{
	unsigned int reg32;
	unsigned long long reg64;
	int ret;

	if (addr_cells == 1) {
		ret = acpi_getprop_u32(&reg32, dev, name);
		*reg = (phys_addr_t)reg32;
	} else {
		ret = acpi_getprop_u64(&reg64, dev, name);
		*reg = (phys_addr_t)reg64;
	}

	return ret;
}

int acpi_reg_get_op(struct acpi_device *dev, const char *name, struct drol_op *op, int op_size)
{
	int i;
	int ret;
	int reg_cnt;
	int addr_cells = 0;
	char string[PROP_NAME_SIZE] = {0};

	ret = snprintf(string, PROP_NAME_SIZE, "%s.reg_cnt", name);
	if (ret == (PROP_NAME_SIZE - 1)) {
		pr_err("%s: op_name is too long\n", __func__);
		return -ENOMEM;
	}
	ret = acpi_getprop_u32(&reg_cnt, dev, string);
	if (ret)
		return -ENODEV;

	acpi_getprop_u32(&addr_cells, dev, "addr_cells");
	if (!addr_cells_range_ok(addr_cells))
		return -EINVAL;

	for (i = 0; i < reg_cnt; i++) {
		if (i >= op_size)
			return -ENOMEM;

		ret = snprintf(string, PROP_NAME_SIZE, "%s.reg%d.reg", name, i + 1);
		if (ret == (PROP_NAME_SIZE - 1)) {
			pr_err("%s: op_name is too long\n", __func__);
			return -ENOMEM;
		}
		ret = acpi_getprop_reg(addr_cells, &op[i].reg, dev, string);
		if (ret)
			return ret;

		ret = snprintf(string, PROP_NAME_SIZE, "%s.reg%d.op", name, i + 1);
		if (ret == (PROP_NAME_SIZE - 1)) {
			pr_err("%s: op_name is too long\n", __func__);
			return -ENOMEM;
		}
		acpi_getprop_op(&op[i].op, dev, string);

		ret = snprintf(string, PROP_NAME_SIZE, "%s.reg%d.clear", name, i + 1);
		if (ret == (PROP_NAME_SIZE - 1)) {
			pr_err("%s: op_name is too long\n", __func__);
			return -ENOMEM;
		}
		acpi_getprop_u32(&op[i].clear, dev, string);

		ret = snprintf(string, PROP_NAME_SIZE, "%s.reg%d.set", name, i + 1);
		if (ret == (PROP_NAME_SIZE - 1)) {
			pr_err("%s: op_name is too long\n", __func__);
			return -ENOMEM;
		}
		acpi_getprop_u32(&op[i].set, dev, string);

		ret = snprintf(string, PROP_NAME_SIZE, "%s.reg%d.xor", name, i + 1);
		if (ret == (PROP_NAME_SIZE - 1)) {
			pr_err("%s: op_name is too long\n", __func__);
			return -ENOMEM;
		}
		acpi_getprop_u32(&op[i].xor, dev, string);

		ret = snprintf(string, PROP_NAME_SIZE, "%s.reg%d.delay", name, i + 1);
		if (ret == (PROP_NAME_SIZE - 1)) {
			pr_err("%s: op_name is too long\n", __func__);
			return -ENOMEM;
		}
		acpi_getprop_u32(&op[i].delay, dev, string);
	}
	return i;
}
#endif

